using System;
using FrameworkX.ObjectSpace.Data.Schema;
using FrameworkX.ObjectSpace.Util;

namespace FrameworkX.ObjectSpace
{
	/// <summary>
	/// Default implementation of object populator. Populates an 
	/// object from a IDataReader object. If anyone wants to populate
	/// thir own objects they should use their own class that 
	/// implements IPopulateObject along with CustomObjectPopulatorAttribute.
	/// </summary>
	public class ObjectPopulator : IPopulateObject
	{
		private PropertyMaps _mapping = null;

		/// <summary>
		/// ObjectPopulator constructor
		/// </summary>
		/// <param name="mapping">Mappings to use for an object</param>
		public ObjectPopulator(PropertyMaps mapping)
		{
			_mapping = mapping;
		} 
		
		#region IPopulateObject Members

		/// <summary>
		/// The defualt logic of populating an object.
		/// </summary>
		/// <param name="obj"></param>
		/// <param name="reader"></param>
		public void PopulateObject(object obj, System.Data.IDataReader reader)
		{
            if (_mapping.ContainsEncrtypedProperties)
            {
                this.PopulateObjectInternal(obj, reader);
            }
            else
            {
                this.PopulateObjectWithEncryptedPropertiesInternal(obj, reader);
            }
		}
		#endregion


        private void PopulateObjectInternal(object obj, System.Data.IDataReader reader)
        {

            foreach (object key in _mapping.Keys)
            {
                PropertyMap map = (PropertyMap)_mapping[key];
                int ordinal = -1;
                try
                {
                    ordinal = reader.GetOrdinal(map.ColumnNameAlias);
                }
                catch
                {
                    ordinal = reader.GetOrdinal(map.ColumnName);
                }
                if (ordinal != -1)
                {
                    object value = reader.GetValue(ordinal);
                    if (!(value is DBNull))
                    {
                        // Support for enum
                        if (map.Property.PropertyType.BaseType.Equals(typeof(Enum)))
                        {
                            value = Enum.Parse(map.Property.PropertyType, value.ToString());
                        }
                        map.Property.SetValue(obj, value, null);
                    }
                }
            }
        }

        private void PopulateObjectWithEncryptedPropertiesInternal(object obj, System.Data.IDataReader reader)
        {

            foreach (object key in _mapping.Keys)
            {
                PropertyMap map = (PropertyMap)_mapping[key];
                int ordinal = -1;
                try
                {
                    ordinal = reader.GetOrdinal(map.ColumnName);                    
                }
                catch
                {
                    ordinal = reader.GetOrdinal(map.ColumnNameAlias);
                }
                if (ordinal != -1)
                {
                    object value = reader.GetValue(ordinal);
                    if (!(value is DBNull))
                    {
                        // Support for enum
                        if (map.Property.PropertyType.BaseType.Equals(typeof(Enum)))
                        {
                            value = Enum.Parse(map.Property.PropertyType, value.ToString());
                        }
                        
                        if (value is string)
                        {
                            // Look for encryption
                            if (!map.IsEncrypted)
                            {
                                map.Property.SetValue(obj, value, null);
                            }
                            else
                            {
                                string encryptionKey = map.EncrtyptionKey;
                                if (map.UseEncryptionKeyFromConfig)
                                {
                                    encryptionKey = System.Configuration.ConfigurationSettings.AppSettings[encryptionKey];
                                }
                                AESCryptoHelper helper = new AESCryptoHelper(encryptionKey);
                                string stringValue = helper.Decrypt(value.ToString());                                
                                map.Property.SetValue(obj, stringValue, null);
                                helper = null;
                            }
                        }
                        else
                        {
                            map.Property.SetValue(obj, value, null);
                        }                        
                    }
                }
            }
        }



        public object PopulateObject(System.Data.IDataReader reader)
        {
            throw new NotImplementedException();
        }

        public bool RequiresObjectCreation
        {
            get { return true; }
        }
    }
}
